package cglib;

import com.google.common.collect.Maps;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hubert
 * @date 2018/4/11
 * @description ReflectUtil
 */
public class ReflectUtil {
	static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);

	public static Object getTarget(Object dest, Map<String, Object> addProperties) {
		// get property map
		PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
		PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
		Map<String, Class> propertyMap = Maps.newHashMap();
		for (PropertyDescriptor d : descriptors) {
			if (!"class".equalsIgnoreCase(d.getName())) {
				propertyMap.put(d.getName(), d.getPropertyType());
			}
		}
		// add extra properties
		addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
		// new dynamic bean
		DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
		// add old value
		propertyMap.forEach((k, v) -> {
			try {
				// filter extra properties
				if (!addProperties.containsKey(k)) {
					dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		});
		// add extra value
		addProperties.forEach((k, v) -> {
			try {
				dynamicBean.setValue(k, v);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		});
		Object target = dynamicBean.getTarget();
		return target;
	}

	public static class DynamicBean {
		/**
		 * 目标对象
		 */
		private Object target;

		/**
		 * 属性集合
		 */
		private BeanMap beanMap;

		public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
			this.target = generateBean(superclass, propertyMap);
			this.beanMap = BeanMap.create(this.target);
		}
		/**
		 * bean 添加属性和值
		 *
		 * @param property
		 * @param value
		 */
		public void setValue(String property, Object value) {
			beanMap.put(property, value);
		}

		/**
		 * 获取属性值
		 *
		 * @param property
		 * @return
		 */
		public Object getValue(String property) {
			return beanMap.get(property);
		}

		/**
		 * 获取对象
		 *
		 * @return
		 */
		public Object getTarget() {
			return this.target;
		}


		/**
		 * 根据属性生成对象
		 *
		 * @param superclass
		 * @param propertyMap
		 * @return
		 */
		private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
			BeanGenerator generator = new BeanGenerator();
			if (null != superclass) {
				generator.setSuperclass(superclass);
			}
			BeanGenerator.addProperties(generator, propertyMap);
			return generator.create();
		}
	}

	public static void main(String[] args) {
		LoggerEntity entity = new LoggerEntity();
		entity.setAppName("appname");
		entity.setOperator("add");
		entity.setResult("result");
		entity.setUri("uri");
		entity.setMethod("method");
		Map<String, Object> addProperties = new HashMap() {{
			put("hello", "world");
			put("abc", "123");
		}};
		System.out.println(getTarget(entity, addProperties));
	}
}
